#define QT_NO_EMIT

#include <algorithm/analytical_model/algorithm/cpu/GeometryUtils.h>
#include <algorithm/analytical_model/algorithm/cpu/VectorConvert.h>
#include <algorithm/analytical_model/algorithm/cpu/heliostat/RectangleHeliostat.h>
#include <algorithm/analytical_model/algorithm/cuda/inter_op/Data.h>
#include <algorithm/analytical_model/algorithm/cuda/inter_op/heliostat/RectangleHeliostat.h>
#include <algorithm/analytical_model/algorithm/cuda/inter_op/utils/convert/AllConverts.h>
#include <algorithm/analytical_model/algorithm/cuda/inter_op/utils/convert/heliostat/ConvertHeliostat.h>
#include <algorithm/analytical_model/algorithm/geometry/Utils.h>
#include <algorithm/analytical_model/algorithm/pipeline/AnalyticalPipelineParam.h>
#include <algorithm/analytical_model/io/json/converter/AllConverters.h>
#include <algorithm/analytical_model/io/json/transform/TransformJSON.h>
#include <ui/window/dataVisualizationWindow/SceneRenderer.h>
#include <utils/Format.h>
#include <utils/ParaUtils.h>
#include <utils/Print.h>
#include <utils/Utils.h>
#include <utils/config.h>


#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QQuickWindow>
#include <QSGRendererInterface>

#include <glm/geometric.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/iostreams/stream.hpp>

// #include <oneapi/tbb.h>

#include <array>
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <memory>
#include <numbers>
#include <span>
#include <string_view>
#include <thread>
#include <vector>

using namespace solar;
using namespace solar::cuda::inter_op;
using namespace std::string_view_literals;

namespace solar::internal
{
    constexpr std::string_view vertex_shader =
        R"(
#version 430

layout (location = 0) uniform mat4 pvm;
layout (location = 1) uniform mat4 model;


layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 coord;
layout (location = 3) in vec4 color;

struct v2f{
	vec3 normal;
	vec2 coord;
	vec4 color;
	vec3 position;
};

out v2f vert_out;

void main(){

	vec4 position_ = vec4(position,1);
	vert_out.normal =  mat3(transpose(inverse(model))) * normal;
	vert_out.coord = coord;
	vert_out.color = color;
	vert_out.position = (model * position_).xyz;
	gl_Position = pvm * position_;
}
)";

    constexpr std::string_view fragment_shader =
        R"(
#version 430

// layout (location = 1) uniform sampler2D sampler;
layout (location = 2) uniform vec4 light_position;
layout (location = 3) uniform vec4 light_ambient;
layout (location = 4) uniform vec4 light_diffuse;
layout (location = 5) uniform vec4 light_specular;
layout (location = 6) uniform vec3 eye;
layout (location = 7) uniform vec3 light_direction;

float specular_intensity = 32.0;

out vec4 o;

struct v2f{
	vec3 normal;
	vec2 coord;
	vec4 color;
	vec3 position;
};

in v2f vert_out;

void main() {
	
	vec3 normal = normalize(vert_out.normal);

	vec3 ambient = light_ambient.xyz;

	//vec3 light_direction = normalize(light_position.xyz - vert_out.position);
    vec3 light_direction1 = -light_direction;

	float diffuse_factor = dot(normal,light_direction1);
	vec3 diffuse = vec3(0,0,0);
	vec3 specular = vec3(0,0,0);
	if (diffuse_factor > 0)
	{
        diffuse = light_diffuse.xyz * diffuse_factor ;

		vec3 vertex_eye = normalize(eye - vert_out.position);
		vec3 light_reflect = normalize(reflect(-light_direction1, normal));
        float specular_factor = dot(vertex_eye,light_reflect);
        
		if (specular_factor > 0) 
		{
			specular_factor = pow(specular_factor, specular_intensity);
			specular = light_specular.xyz * specular_factor;
		}
		
	}

	// float distance = length(light_position.xyz - vert_out.position);

    // float attenuation = 10.0 / ( 1 + (distance*distance));  
    float attenuation = 1;
	
	o = vert_out.color * vec4(ambient + (diffuse + specular) * attenuation, 1);
}

)";

    constexpr std::string_view fragment_shader_with_texture =
        R"(
#version 430

layout (location = 2) uniform vec4 light_position;
layout (location = 3) uniform vec4 light_ambient;
layout (location = 4) uniform vec4 light_diffuse;
layout (location = 5) uniform vec4 light_specular;
layout (location = 6) uniform vec3 eye;
layout (location = 7) uniform vec3 light_direction;
layout (location = 8) uniform sampler2D sampler;

float specular_intensity = 32.0;

out vec4 o;

struct v2f{
	vec3 normal;
	vec2 coord;
	vec4 color;
	vec3 position;
};

in v2f vert_out;

void main() {
	
	vec3 normal = normalize(vert_out.normal);

	vec3 ambient = light_ambient.xyz;

	//vec3 light_direction = normalize(light_position.xyz - vert_out.position);
    vec3 light_direction1 = -light_direction;

	float diffuse_factor = dot(normal,light_direction1);
	vec3 diffuse = vec3(0,0,0);
	vec3 specular = vec3(0,0,0);
	if (diffuse_factor > 0)
	{
        diffuse = light_diffuse.xyz * diffuse_factor ;

		vec3 vertex_eye = normalize(eye - vert_out.position);
		vec3 light_reflect = normalize(reflect(-light_direction1, normal));
        float specular_factor = dot(vertex_eye,light_reflect);
        
		if (specular_factor > 0) 
		{
			specular_factor = pow(specular_factor, specular_intensity);
			specular = light_specular.xyz * specular_factor;
		}
		
	}

	// float distance = length(light_position.xyz - vert_out.position);

    // float attenuation = 10.0 / ( 1 + (distance*distance));  
    float attenuation = 1;
	
    if (vert_out.coord.x < 0 || vert_out.coord.y < 0)
    {
        o = vert_out.color * vec4(ambient + (diffuse + specular) * attenuation, 1);
    }
	else
    {
        o = texture(sampler, vert_out.coord) * vec4(ambient + (diffuse + specular) * attenuation, 1);
    }
}

)";

    constexpr std::string_view fragment_shader_with_skybox_reflect_upper_face =
        R"(
#version 430

layout (location = 2) uniform vec4 light_position;
layout (location = 3) uniform vec4 light_ambient;
layout (location = 4) uniform vec4 light_diffuse;
layout (location = 5) uniform vec4 light_specular;
layout (location = 6) uniform vec3 eye;
layout (location = 7) uniform vec3 light_direction;
layout (location = 8) uniform samplerCube skybox;

float specular_intensity = 32.0;

out vec4 o;

struct v2f{
	vec3 normal;
	vec2 coord;
	vec4 color;
	vec3 position;
};

in v2f vert_out;

void main() {
	
	vec3 normal = normalize(vert_out.normal);

	vec3 ambient = light_ambient.xyz;

	//vec3 light_direction = normalize(light_position.xyz - vert_out.position);
    vec3 light_direction1 = -light_direction;

	float diffuse_factor = dot(normal,light_direction1);
	vec3 diffuse = vec3(0,0,0);
	vec3 specular = vec3(0,0,0);
	if (diffuse_factor > 0)
	{
        diffuse = light_diffuse.xyz * diffuse_factor ;

		vec3 vertex_eye = normalize(eye - vert_out.position);
		vec3 light_reflect = normalize(reflect(-light_direction1, normal));
        float specular_factor = dot(vertex_eye,light_reflect);
        
		if (specular_factor > 0) 
		{
			specular_factor = pow(specular_factor, specular_intensity);
			specular = light_specular.xyz * specular_factor;
		}
		
	}

	// float distance = length(light_position.xyz - vert_out.position);

    // float attenuation = 10.0 / ( 1 + (distance*distance));  
    float attenuation = 1;

    vec3 I = normalize(vert_out.position - eye);
    vec3 R = reflect(I, normal);
    
    if (dot(normal, vec3(0, 1, 0)) > 0)
    {
        o = vec4(texture(skybox, R).rgb, 1.0) * vec4(ambient + (diffuse + specular) * attenuation, 1);
    }
    else
    {
        o = vert_out.color * vec4(ambient + (diffuse + specular) * attenuation, 1);
    }
	
    //if (vert_out.coord.x < 0 || vert_out.coord.y < 0)
    //{
    //    o = vert_out.color * vec4(ambient + (diffuse + specular) * attenuation, 1);
    //}
	//else
    //{
    //    o = texture(sampler, vert_out.coord) * vec4(ambient + (diffuse + specular) * attenuation, 1);
    //}
}

)";

    constexpr std::string_view skybox_vertex_shader =
        R"(
#version 330 core
layout (location = 0) in vec3 aPos;
 
out vec3 TexCoords;
 
uniform mat4 projection;
uniform mat4 view;
// uniform mat4 model;
 
void main()
{
    TexCoords = aPos;
    vec4 pos = projection * view * vec4(aPos, 1.0);
    gl_Position = pos.xyww;
}

)";

    constexpr std::string_view skybox_fragment_shader =
        R"(
#version 330 core
out vec4 FragColor;
 
in vec3 TexCoords;
 
uniform samplerCube skybox;
 
void main()
{    
    FragColor = texture(skybox, TexCoords);
}

)";

    static constexpr std::array skybox_vertices = {
        // 位置
        -1.0F, 1.0F,  -1.0F, -1.0F, -1.0F, -1.0F, 1.0F,  -1.0F, -1.0F,
        1.0F,  -1.0F, -1.0F, 1.0F,  1.0F,  -1.0F, -1.0F, 1.0F,  -1.0F,

        -1.0F, -1.0F, 1.0F,  -1.0F, -1.0F, -1.0F, -1.0F, 1.0F,  -1.0F,
        -1.0F, 1.0F,  -1.0F, -1.0F, 1.0F,  1.0F,  -1.0F, -1.0F, 1.0F,

        1.0F,  -1.0F, -1.0F, 1.0F,  -1.0F, 1.0F,  1.0F,  1.0F,  1.0F,
        1.0F,  1.0F,  1.0F,  1.0F,  1.0F,  -1.0F, 1.0F,  -1.0F, -1.0F,

        -1.0F, -1.0F, 1.0F,  -1.0F, 1.0F,  1.0F,  1.0F,  1.0F,  1.0F,
        1.0F,  1.0F,  1.0F,  1.0F,  -1.0F, 1.0F,  -1.0F, -1.0F, 1.0F,

        -1.0F, 1.0F,  -1.0F, 1.0F,  1.0F,  -1.0F, 1.0F,  1.0F,  1.0F,
        1.0F,  1.0F,  1.0F,  -1.0F, 1.0F,  1.0F,  -1.0F, 1.0F,  -1.0F,

        -1.0F, -1.0F, -1.0F, -1.0F, -1.0F, 1.0F,  1.0F,  -1.0F, -1.0F,
        1.0F,  -1.0F, -1.0F, -1.0F, -1.0F, 1.0F,  1.0F,  -1.0F, 1.0F};

    static constexpr std::array skybox_texture_names = {
        ":/image/scene_renderer_icon/skybox/right.jpg",
        ":/image/scene_renderer_icon/skybox/left.jpg",
        ":/image/scene_renderer_icon/skybox/top.jpg",
        ":/image/scene_renderer_icon/skybox/bottom.jpg",
        ":/image/scene_renderer_icon/skybox/back.jpg",
        ":/image/scene_renderer_icon/skybox/front.jpg"};

    static auto localToWorldRotateMatrix(const Vec3& normal) -> glm::mat4
    {
        Vec3 n = normal;
        Vec3 u = cross({0, 1, 0}, n).get_unit_vector();
        Vec3 v = cross(u, n).get_unit_vector();
        auto u_data = u.data();
        auto n_data = n.data();
        auto v_data = v.data();
        std::array<float, 16> mat4_array = {(float)u_data[0],
                                            (float)u_data[1],
                                            (float)u_data[2],
                                            0,
                                            (float)n_data[0],
                                            (float)n_data[1],
                                            (float)n_data[2],
                                            0,
                                            (float)v_data[0],
                                            (float)v_data[1],
                                            (float)v_data[2],
                                            0,
                                            0,
                                            0,
                                            0,
                                            1};
        return glm::make_mat4(mat4_array.data());
    }

    // static auto toQVector4D(const glm::vec4& input) -> QVector4D
    // {
    //     const std::span data(glm::value_ptr(input), 4);
    //     return {data[0], data[1], data[2], data[3]};
    // }

    // static auto toQVector3D(const glm::vec3& input) -> QVector3D
    // {
    //     const std::span data(glm::value_ptr(input), 3);
    //     return {data[0], data[1], data[2]};
    // }

    // static auto toQMatrix4x4(const glm::mat4& input) -> QMatrix4x4
    // {
    //     // glm::mat4：列优先存储，QMatrix4x4：行优先存储
    //     // auto input1 = glm::transpose(input);
    //     const std::span data(glm::value_ptr(input), 16);
    //     return {data[0], data[1], data[2],  data[3],  data[4],  data[5],  data[6],  data[7],
    //             data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]};
    // }
} // namespace solar::internal

SceneRenderer::SceneRenderer()
    : program_(nullptr), program_with_texture_(nullptr), program_skybox_(nullptr),
      program_skybox_reflect_upper_face_(nullptr), window_(nullptr), receiver_texture_index_(),
      skybox_texture_index_()
{
}

SceneRenderer::~SceneRenderer()
{
    delete this->program_;
    delete this->program_with_texture_;
    delete this->program_skybox_;
    delete this->program_skybox_reflect_upper_face_;
    this->program_ = nullptr;
    this->program_with_texture_ = nullptr;
    this->program_skybox_ = nullptr;
    this->program_skybox_reflect_upper_face_ = nullptr;
}

void SceneRenderer::setViewportSize(const QSize& size) { this->viewport_size_ = size; }

void SceneRenderer::setWindow(QQuickWindow* window) { this->window_ = window; }

constexpr void SceneRenderer::generatePolygonVertexPositions(int num_edges, float* buffer)
{
    constexpr auto to_radian = []<std::floating_point T>(T degree)
    { return degree * std::numbers::pi_v<T> / static_cast<T>(180.0); };
    float degree = 360.0F / num_edges;
    // double half_degree = degree / 2.0;
    for (std::ptrdiff_t i = 0; i < num_edges; ++i)
    {
        buffer[i * 2] = const_expr::cos(to_radian(static_cast<float>(degree * i))); // x
        buffer[i * 2 + 1] = const_expr::sin(to_radian(degree * i));                 // z
    }
}

constexpr void SceneRenderer::generatePolygonFaceNormals(int num_edges, float* buffer)
{
    constexpr auto to_radian = []<std::floating_point T>(T degree)
    { return degree * std::numbers::pi_v<T> / static_cast<T>(180.0); };
    float degree = 360.0F / num_edges;
    float half_degree = degree / 2.0F;
    for (std::ptrdiff_t i = 0; i < num_edges; ++i)
    {
        buffer[i * 2] =
            const_expr::cos(to_radian(static_cast<float>(half_degree + degree * i))); // x
        buffer[i * 2 + 1] = const_expr::sin(to_radian(half_degree + degree * i));     // z
    }
}

constexpr void SceneRenderer::generatePolygonVertices1(int num_edges,
                                                       const float* __restrict positions,
                                                       const float* __restrict normals,
                                                       Vertex* __restrict output)
{
    const auto tex_coord_step = 1.0F / num_edges;
    for (std::ptrdiff_t i = 0; i < num_edges - 1; i++)
    {
        const auto current_tex_coord_x = i * tex_coord_step;
        const auto current_face_begin_index = i * 4;
        output[current_face_begin_index] = {
            {positions[i * 2], -0.5, positions[i * 2 + 1]},
            {normals[i * 2], 0, normals[i * 2 + 1]},
            {current_tex_coord_x, 0},
            {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
        };
        output[current_face_begin_index + 3] = {
            {positions[i * 2], 0.5, positions[i * 2 + 1]},
            {normals[i * 2], 0, normals[i * 2 + 1]},
            {current_tex_coord_x, 1},
            {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
        };
        output[current_face_begin_index + 2] = {
            {positions[(i + 1) * 2], 0.5, positions[(i + 1) * 2 + 1]},
            {normals[i * 2], 0, normals[i * 2 + 1]},
            {current_tex_coord_x + tex_coord_step, 1},
            {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
        };
        output[current_face_begin_index + 1] = {
            {positions[(i + 1) * 2], -0.5, positions[(i + 1) * 2 + 1]},
            {normals[i * 2], 0, normals[i * 2 + 1]},
            {current_tex_coord_x + tex_coord_step, 0},
            {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
        };
    }

    // Avoid using mod in loop to improve speed

    const int last = num_edges - 1;
    const auto current_tex_coord_x = last * tex_coord_step;
    const auto current_face_begin_index = last * 4;
    output[current_face_begin_index] = {
        {positions[last * 2], -0.5, positions[last * 2 + 1]},
        {normals[last * 2], 0, normals[last * 2 + 1]},
        {current_tex_coord_x, 0},
        {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
    };
    output[current_face_begin_index + 3] = {
        {positions[last * 2], 0.5, positions[last * 2 + 1]},
        {normals[last * 2], 0, normals[last * 2 + 1]},
        {current_tex_coord_x, 1},
        {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
    };
    output[current_face_begin_index + 2] = {
        {positions[0], 0.5, positions[1]},
        {normals[last * 2], 0, normals[last * 2 + 1]},
        {current_tex_coord_x + tex_coord_step, 1},
        {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
    };
    output[current_face_begin_index + 1] = {
        {positions[0], -0.5, positions[1]},
        {normals[last * 2], 0, normals[last * 2 + 1]},
        {current_tex_coord_x + tex_coord_step, 0},
        {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
    };
}

constexpr void SceneRenderer::generatePolygonCeilAndFloorVertices1(
    int num_edges, const float* __restrict positions, Vertex* __restrict output)
{
    output[0] = {
        {0, 0.5F, 0},
        {0, 1, 0},
        {-1, -1},
        {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
    };
    output[num_edges + 1] = {
        {0, -0.5F, 0},
        {0, 1, 0},
        {-1, -1},
        {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
    };
    for (std::ptrdiff_t i = 1, j = 0; j < num_edges; i++, j++)
    {
        output[i] = {
            {positions[j * 2], 0.5, positions[j * 2 + 1]},
            {0, 1, 0},
            {-1, -1},
            {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
        };
    }
    for (std::ptrdiff_t i = num_edges + 2, j = 0; j < num_edges; i++, j++)
    {
        output[i] = {
            {positions[j * 2], -0.5, positions[j * 2 + 1]},
            {0, -1, 0},
            {-1, -1},
            {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
        };
    }
}

template <std::size_t N>
consteval auto SceneRenderer::generatePolygonVertices() -> std::array<Vertex, N * 4>
{
    std::array<Vertex, N * 4> result;
    std::array<float, N * 2> polygon_vertices;
    std::array<float, N * 2> polygon_normals;
    generatePolygonVertexPositions(N, polygon_vertices.data());
    generatePolygonFaceNormals(N, polygon_normals.data());
    generatePolygonVertices1(N, polygon_vertices.data(), polygon_normals.data(), result.data());
    return result;
}

constexpr void SceneRenderer::generatePolygonVertexIndicesInternal(int num_edges, GLuint* output)
{
    static constexpr std::array indices_0 = {0U, 1U, 3U, 2U, 3U, 1U};
    static constexpr auto num_face_vertices = 4;
    for (std::ptrdiff_t i = 0; i < num_edges; i++)
    {
        const auto begin_index = i * indices_0.size();
        for (std::ptrdiff_t j = 0; j < indices_0.size(); j++)
        {
            output[begin_index + j] = indices_0[j] + i * num_face_vertices;
        }
    }
}

constexpr void SceneRenderer::generatePolygonFullVertexIndicesInternal(int num_edges,
                                                                       GLuint* output)
{
    static constexpr std::array indices_0 = {0U, 1U, 3U, 2U, 3U, 1U};
    static constexpr auto num_face_vertices = 4;

    // 侧面的索引

    for (std::ptrdiff_t i = 0; i < num_edges; i++)
    {
        const auto begin_index = i * indices_0.size();
        for (std::ptrdiff_t j = 0; j < indices_0.size(); j++)
        {
            output[begin_index + j] = indices_0[j] + i * num_face_vertices;
        }
    }

    // 顶面、底面的索引

    const auto ceil_center_index = num_face_vertices * num_edges;
    const auto floor_center_index = ceil_center_index + num_edges + 1;
    for (std::ptrdiff_t j = 0; j < num_edges - 1; j++)
    {
        output[num_edges * indices_0.size() + j * 3] = ceil_center_index;
        output[num_edges * indices_0.size() + j * 3 + 1] = ceil_center_index + 1 + j;
        output[num_edges * indices_0.size() + j * 3 + 2] = ceil_center_index + 1 + (1 + j);
        output[num_edges * (indices_0.size() + 3) + j * 3] = floor_center_index;
        output[num_edges * (indices_0.size() + 3) + j * 3 + 1] = floor_center_index + 1 + j;
        output[num_edges * (indices_0.size() + 3) + j * 3 + 2] = floor_center_index + 1 + (1 + j);
    }
    output[num_edges * indices_0.size() + (num_edges - 1) * 3] = ceil_center_index;
    output[num_edges * indices_0.size() + (num_edges - 1) * 3 + 1] =
        ceil_center_index + 1 + (num_edges - 1);
    output[num_edges * indices_0.size() + (num_edges - 1) * 3 + 2] = ceil_center_index + 1;
    output[num_edges * (indices_0.size() + 3) + (num_edges - 1) * 3] = floor_center_index;
    output[num_edges * (indices_0.size() + 3) + (num_edges - 1) * 3 + 1] =
        floor_center_index + 1 + (num_edges - 1);
    output[num_edges * (indices_0.size() + 3) + (num_edges - 1) * 3 + 2] = floor_center_index + 1;
}

template <std::size_t N>
consteval auto SceneRenderer::generatePolygonVertexIndices() -> std::array<GLuint, N * 6>
{
    static constexpr std::array indices_0 = {0U, 1U, 3U, 2U, 3U, 1U};
    std::array<GLuint, N * indices_0.size()> result;
    generatePolygonVertexIndicesInternal(N, result.data());
    return result;
}

template <std::size_t N>
consteval auto SceneRenderer::generatePolygonFullVertices() -> std::array<Vertex, N * 6 + 2>
{
    std::array<Vertex, N * 6 + 2> result;
    std::array<float, N * 2> polygon_vertices;
    std::array<float, N * 2> polygon_normals;
    generatePolygonVertexPositions(N, polygon_vertices.data());
    generatePolygonFaceNormals(N, polygon_normals.data());
    generatePolygonVertices1(N, polygon_vertices.data(), polygon_normals.data(), result.data());
    generatePolygonCeilAndFloorVertices1(N, polygon_vertices.data(), result.data() + N * 4);
    return result;
}

template <std::size_t N>
consteval auto SceneRenderer::generatePolygonFullVertexIndices() -> std::array<GLuint, N * 12>
{
    std::array<GLuint, N * 12> result;
    generatePolygonFullVertexIndicesInternal(N, result.data());
    return result;
}

template <ConstSpanLike VerticesContainer, ConstSpanLike IndicesContainer>
void SceneRenderer::loadOpenGLFaceVertices(OpenGLBufferIndex& index,
                                           const VerticesContainer& vertices_container,
                                           const IndicesContainer& indices_container)
{
    glGenVertexArrays(1, std::addressof(index.vao_));
    glGenBuffers(1, std::addressof(index.vbo_));
    glGenBuffers(1, std::addressof(index.ebo_));
    glBindVertexArray(index.vao_);
    glBindBuffer(GL_ARRAY_BUFFER, index.vbo_);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices_container.size(),
                 vertices_container.data(), GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, Vertex::position_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, Vertex::normal_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex),
                          (const void*)(sizeof(float[Vertex::position_size_])));
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(
        2, Vertex::coord_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex),
        (const void*)(sizeof(float[Vertex::position_size_ + Vertex::normal_size_])));
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(
        3, Vertex::color_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex),
        (const void*)(sizeof(
            float[Vertex::position_size_ + Vertex::normal_size_ + Vertex::coord_size_])));
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index.ebo_);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices_container.size(),
                 indices_container.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

template <ConstSpanLike VerticesContainer>
void SceneRenderer::loadOpenGLLineVertices(OpenGLBufferIndex& index,
                                           const VerticesContainer& vertices_container)
{
    glGenVertexArrays(1, std::addressof(index.vao_));
    glGenBuffers(1, std::addressof(index.vbo_));
    glBindVertexArray(index.vao_);
    glBindBuffer(GL_ARRAY_BUFFER, index.vbo_);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices_container.size(),
                 vertices_container.data(), GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, Vertex::position_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, Vertex::normal_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex),
                          (const void*)(sizeof(float[Vertex::position_size_])));
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(
        2, Vertex::coord_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex),
        (const void*)(sizeof(float[Vertex::position_size_ + Vertex::normal_size_])));
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(
        3, Vertex::color_size_, GL_FLOAT, GL_FALSE, sizeof(Vertex),
        (const void*)(sizeof(
            float[Vertex::position_size_ + Vertex::normal_size_ + Vertex::coord_size_])));
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

template <ConstSpanLike TextureFilePathContainer>
void SceneRenderer::loadCubemapTexture(const TextureFilePathContainer& paths)
{
    glGenTextures(1, std::addressof(skybox_texture_index_));
    glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_index_);
    {
        for (int i = 0; i < paths.size(); i++)
        {
            QImage cube_texture_image(paths[i]);
            const QImage cube_texture_image1 =
                cube_texture_image.convertedTo(QImage::Format_RGB888);
            const auto* texture_data = cube_texture_image1.bits();
            // std::cout << solar::format("({}, {})\n", cube_texture_image1.width(),
            // cube_texture_image1.height());
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, cube_texture_image1.width(),
                         cube_texture_image1.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data);
        }
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    }
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

constexpr void SceneRenderer::generateSphereVerticesInternal(int segments, Vertex* output)
{
    std::ptrdiff_t index = 0;
    for (std::ptrdiff_t y = 0; y <= segments; y++)
    {
        for (std::ptrdiff_t x = 0; x <= segments; x++)
        {
            float x_segment = static_cast<float>(x) / segments;
            float y_segment = static_cast<float>(y) / segments;
            float x_pos = const_expr::cos(x_segment * 2.0F * std::numbers::pi_v<float>) *
                          const_expr::sin(y_segment * std::numbers::pi_v<float>);
            float y_pos = const_expr::cos(y_segment * std::numbers::pi_v<float>);
            float z_pos = const_expr::sin(x_segment * 2.0F * std::numbers::pi_v<float>) *
                          const_expr::sin(y_segment * std::numbers::pi_v<float>);
            Vertex current_vertex = {
                {x_pos, y_pos, z_pos},
                {0, 0, 0},
                {-1, -1},
                {235.0 / 255, 208.0 / 255, 166.0 / 255, 1}
            };
            output[index] = current_vertex;
            index++;
        }
    }
}

constexpr void SceneRenderer::generateSphereIndicesInternal(int segments, GLuint* output)
{
    std::ptrdiff_t index = 0;
    for (std::ptrdiff_t i = 0; i < segments; i++)
    {
        for (std::ptrdiff_t j = 0; j < segments; j++)
        {
            output[index] = i * (segments + 1) + j;
            output[index + 1] = (i + 1) * (segments + 1) + j;
            output[index + 2] = (i + 1) * (segments + 1) + j + 1;
            output[index + 3] = i * (segments + 1) + j;
            output[index + 4] = (i + 1) * (segments + 1) + j + 1;
            output[index + 5] = i * (segments + 1) + j + 1;
            index += 6;
        }
    }
}

template <std::size_t N>
consteval auto SceneRenderer::generateSphereVertices() -> std::array<Vertex, (N + 1) * (N + 1)>
{
    std::array<Vertex, (N + 1) * (N + 1)> sphere_vertices;
    generateSphereVerticesInternal(N, sphere_vertices.data());
    return sphere_vertices;
}

template <std::size_t N>
consteval auto SceneRenderer::generateSphereIndices() -> std::array<GLuint, 6 * N * N>
{
    std::array<GLuint, 6 * N * N> sphere_indices;
    generateSphereIndicesInternal(N, sphere_indices.data());
    return sphere_indices;
}

template <StringLike Shader1, StringLike Shader2>
void SceneRenderer::initOpenGLShaderProgram(QOpenGLShaderProgram* shader_program,
                                            const Shader1& vertex_shader,
                                            const Shader2& fragment_shader)
{
    if (shader_program != nullptr)
    {
        shader_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex,
                                                         vertex_shader.data());
        shader_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment,
                                                         fragment_shader.data());
        if (const auto program_log = shader_program->log(); !program_log.isEmpty())
        {
            std::cout << program_log.toStdString() << '\n';
        }
        try
        {
            shader_program->link();
        }
        catch (...)
        {
            std::cout << shader_program->log().toStdString() << '\n';
            std::abort();
        }
    }
}

void SceneRenderer::init()
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    if (program_ == nullptr)
    {
        render_lock.lock();
        QSGRendererInterface* rif = window_->rendererInterface();
        Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::OpenGL);

        initializeOpenGLFunctions();

        program_ = new QOpenGLShaderProgram();
        initOpenGLShaderProgram(program_, internal::vertex_shader, internal::fragment_shader);
        program_with_texture_ = new QOpenGLShaderProgram();
        initOpenGLShaderProgram(program_with_texture_, internal::vertex_shader,
                                internal::fragment_shader_with_texture);
        program_skybox_ = new QOpenGLShaderProgram();
        initOpenGLShaderProgram(program_skybox_, internal::skybox_vertex_shader,
                                internal::skybox_fragment_shader);
        program_skybox_reflect_upper_face_ = new QOpenGLShaderProgram();
        initOpenGLShaderProgram(program_skybox_reflect_upper_face_, internal::vertex_shader,
                                internal::fragment_shader_with_skybox_reflect_upper_face);

        loadOpenGLLineVertices(axis_index_, axis_vertices_);

        static constexpr auto cylinder_vertices = generatePolygonFullVertices<72>();
        static constexpr auto cylinder_indices = generatePolygonFullVertexIndices<72>();
        // static constexpr auto prism6_vertices = generatePolygonFullVertices<6>();
        // static constexpr auto prism6_indices = generatePolygonFullVertexIndices<6>();

        loadOpenGLFaceVertices(receiver_pillar_index_, cylinder_vertices, cylinder_indices);

        loadOpenGLFaceVertices(ground_index_, ground_vertices_, rectangle_indices_);

        // sun
        // 球体太阳

        static constexpr auto sun_vertices = generateSphereVertices<72>();
        static constexpr auto sun_indices = generateSphereIndices<72>();
        loadOpenGLFaceVertices(sun_index_, sun_vertices, sun_indices);

        // skybox

        glGenVertexArrays(1, std::addressof(skybox_index_.vao_));
        glGenBuffers(1, std::addressof(skybox_index_.vbo_));
        glBindVertexArray(skybox_index_.vao_);
        glBindBuffer(GL_ARRAY_BUFFER, skybox_index_.vbo_);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * internal::skybox_vertices.size(),
                     internal::skybox_vertices.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);

        loadCubemapTexture(internal::skybox_texture_names);

        render_lock.unlock();
    }
}

void SceneRenderer::paint()
{
    // Play nice with the RHI. Not strictly needed when the scenegraph uses
    // OpenGL directly.
    window_->beginExternalCommands();

    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    render_lock.lock();

    if (this->scene_updated_)
    {
        deleteAllOpenGLBufferIndices();
        updateSceneInternal();
    }

    if (this->scene_updated_for_sunray_)
    {
        // deleteOpenGLBufferIndicesForSunray();
        updateSceneForSunrayInternal();
    }

    program_->bind();
    glBindVertexArray(NULL);

    // Begin OpenGL Specific functions

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glPolygonMode(GL_FRONT, GL_FILL);
    glEnable(GL_CULL_FACE);
    // glCullFace(GL_BACK);
    glDisable(GL_BLEND);
    glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
    glViewport(0, 0, viewport_size_.width(), viewport_size_.height());
    // 打开抗锯齿功能
    // 1.开启混合功能
    // glEnable(GL_BLEND);

    // 2.指定混合因子
    // 注意:如果你修改了混合方程式,当你使用混合抗锯齿功能时,请一定要改为默认混合方程式
    // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // // 3.开启对点\线\多边形的抗锯齿功能
    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_POLYGON_SMOOTH);
    glEnable(GL_MULTISAMPLE);

    glUseProgram(program_->programId());
    light_ambient_ = {0.3, 0.3, 0.3, 1};
    glUniform4fv(2, 1, glm::value_ptr(light_position_));
    glUniform4fv(3, 1, glm::value_ptr(light_ambient_));
    glUniform4fv(4, 1, glm::value_ptr(light_diffuse_));
    glUniform4fv(5, 1, glm::value_ptr(light_specular_));
    glUniform3fv(6, 1, glm::value_ptr(eye_));
    glUniform3fv(7, 1, glm::value_ptr(light_direction_));
    // 接收器中心看向第一个定日镜中心
    glm::mat4 view = glm::lookAt(eye_, center_, glm::vec3(0, 1, 0));
    glm::mat4 model_1(1.0F);
    glm::mat4 persp = glm::perspective(
        glm::radians(45.0F), static_cast<float>(viewport_size_.width()) / viewport_size_.height(),
        0.1F, 10000.0F);
    // auto pvm1 = persp * view * model_1;

    // draw heliostat pillars;
    heliostat_pillar_transform_mats_.resize(all_rectangle_heliostat_centers_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        auto current_center = all_rectangle_heliostat_centers_[i];
        glm::mat4 scale_mat_heliostat_pillar =
            glm::scale(model_1, {0.1, (current_center.y - 0.1), 0.1});
        auto model_heliostat_pillar = glm::translate(
            model_1, glm::vec3(current_center.x, (current_center.y - 0.1) / 2.0, current_center.z));
        model_heliostat_pillar = model_heliostat_pillar * scale_mat_heliostat_pillar;
        auto pvm_heliostat_pillar = persp * view * model_heliostat_pillar;
        heliostat_pillar_transform_mats_[i] =
            std::make_pair(pvm_heliostat_pillar, model_heliostat_pillar);
    }
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        const auto& buffer_index = heliostat_pillar_indices_[i];
        glBindVertexArray(buffer_index.vao_);
        glUniformMatrix4fv(0, 1, GL_FALSE,
                           glm::value_ptr(heliostat_pillar_transform_mats_[i].first));
        glUniformMatrix4fv(1, 1, GL_FALSE,
                           glm::value_ptr(heliostat_pillar_transform_mats_[i].second));
        glDrawElements(GL_TRIANGLES, rectangle3d_indices_.size(), GL_UNSIGNED_INT, nullptr);
        glBindVertexArray(NULL);
    }

    // draw receiver pillar
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    auto scale_receiver_y = receiver_center_.y - receiver_size_.y / 2.0;
    glm::mat4 scale_mat_receiver_pillar = glm::scale(model_1, {2.5, scale_receiver_y, 2.5});
    auto model_receiver_pillar = glm::translate(
        model_1, glm::vec3(receiver_center_.x, scale_receiver_y / 2.0, receiver_center_.z));
    model_receiver_pillar = model_receiver_pillar * scale_mat_receiver_pillar;
    auto pvm_receiver_pillar = persp * view * model_receiver_pillar;
    glBindVertexArray(receiver_pillar_index_.vao_);
    glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(pvm_receiver_pillar));
    glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(model_receiver_pillar));
    glDrawElements(GL_TRIANGLES, 72 * 12, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(NULL);
    glCullFace(GL_BACK);

    // draw ground
    glDisable(GL_CULL_FACE);
    auto ground_size = cpu::toFloat3(SOLAR_GET_VALUE(RectangleGrid, size_, rectangle_grid_));
    auto ground_position = SOLAR_GET_VALUE(RectangleGrid, position_, rectangle_grid_);
    auto ground_center = cpu::toFloat3(
        ground_position + SOLAR_GET_VALUE(RectangleGrid, size_, rectangle_grid_) / 2.0);
    glm::mat4 scale_mat_ground = glm::scale(model_1, {ground_size.x, 1, ground_size.z});
    auto model_ground = glm::translate(model_1, {ground_center.x, 0, ground_center.z});
    model_ground = model_ground * scale_mat_ground;
    auto pvm_ground = persp * view * model_ground;
    glBindVertexArray(ground_index_.vao_);
    glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(pvm_ground));
    glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(model_ground));
    glDrawElements(GL_TRIANGLES, rectangle_indices_.size(), GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(NULL);

    // draw sun

    glUniform4fv(3, 1, glm::value_ptr(glm::vec3(1, 1, 1)));
    glm::mat4 scale_mat_sun = glm::scale(model_1, {10, 10, 10});
    auto model_sun = glm::translate(model_1, {400, 400, 400});
    model_sun = model_sun * scale_mat_sun;
    auto pvm_sun = persp * view * model_sun;
    glBindVertexArray(sun_index_.vao_);
    glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(pvm_sun));
    glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(model_sun));
    glDrawElements(GL_TRIANGLES, 6 * 72 * 72, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(NULL);
    glUniform4fv(3, 1, glm::value_ptr(light_ambient_));

    // draw heliostats
    program_->release();
    program_skybox_reflect_upper_face_->bind();
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glUniform4fv(2, 1, glm::value_ptr(light_position_));
    glUniform4fv(3, 1, glm::value_ptr(light_ambient_));
    glUniform4fv(4, 1, glm::value_ptr(light_diffuse_));
    glUniform4fv(5, 1, glm::value_ptr(light_specular_));
    glUniform3fv(6, 1, glm::value_ptr(eye_));
    glUniform3fv(7, 1, glm::value_ptr(light_direction_));
    glUniform1i(8, 0);
    heliostat_transform_mats_.resize(heliostat_buffer_indices_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < heliostat_buffer_indices_.size(); i++)
    {
        glm::mat4 rotate_mat_heliostat =
            internal::localToWorldRotateMatrix(cpu::toVec3(all_rectangle_heliostat_normals_[i]));
        glm::mat4 scale_mat_heliostat =
            glm::scale(model_1, structCopy<glm::vec3>(all_rectangle_heliostat_sizes_[i]));
        glm::mat4 translate_mat_heliostat =
            glm::translate(model_1, structCopy<glm::vec3>(all_rectangle_heliostat_centers_[i]));
        auto model_heliostat = translate_mat_heliostat * rotate_mat_heliostat * scale_mat_heliostat;
        auto pvm_heliostat = persp * view * model_heliostat;
        heliostat_transform_mats_[i] = std::make_pair(pvm_heliostat, model_heliostat);
    }
    for (int i = 0; i < heliostat_buffer_indices_.size(); i++)
    {
        const auto& buffer_index = heliostat_buffer_indices_[i];
        glBindVertexArray(buffer_index.vao_);
        glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(heliostat_transform_mats_[i].first));
        glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(heliostat_transform_mats_[i].second));
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_index_);
        glDrawElements(GL_TRIANGLES, rectangle3d_indices_.size(), GL_UNSIGNED_INT, nullptr);
        glBindVertexArray(NULL);
    }
    glCullFace(GL_BACK);
    program_skybox_reflect_upper_face_->release();

    // draw receiver [矩形]
    // glDisable(GL_CULL_FACE);
    // glm::mat4 scale_mat_receiver = glm::scale(model_1, {40, 20, 1});
    // auto model_receiver = glm::translate(model_1, glm::vec3(0, 180, 0));
    // model_receiver = model_receiver * scale_mat_receiver;
    // auto pvm_receiver = persp * view * model_receiver;
    // glBindVertexArray(receiver_index_.vao_);
    // glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(pvm_receiver));
    // glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(model_receiver));
    // glDrawElements(GL_TRIANGLES, rectangle_indices_.size(), GL_UNSIGNED_INT, nullptr);
    // glBindVertexArray(NULL);

    // draw receiver
    program_->release();
    program_with_texture_->bind();
    glUniform4fv(2, 1, glm::value_ptr(light_position_));
    glUniform4fv(3, 1, glm::value_ptr(light_ambient_));
    glUniform4fv(4, 1, glm::value_ptr(light_diffuse_));
    glUniform4fv(5, 1, glm::value_ptr(light_specular_));
    glUniform3fv(6, 1, glm::value_ptr(eye_));
    glUniform3fv(7, 1, glm::value_ptr(light_direction_));

    glDisable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    // glm::mat4 scale_mat_receiver = glm::scale(model_1, {10, 20, 10});
    glm::mat4 scale_mat_receiver;
    switch (this->receiver_geometry_type_)
    {
    case GeometryType::kGeometryRectangle3D:
        scale_mat_receiver = glm::scale(model_1, structCopy<glm::vec3>(receiver_size_));
        break;
    case GeometryType::kGeometryCylinder3D:
        scale_mat_receiver =
            glm::scale(model_1, {receiver_size_.x / 2, receiver_size_.y, receiver_size_.z / 2});
        break;
        // default:
        // {
        //     const auto error_string =
        //         ::solar::format("ERROR: In function {}:\n {} is not implemented",
        //         PRETTY_FUNCTION_NAME,
        //                         solar::getEnumName(this->receiver_geometry_type_));
        //     solar::println(error_string);
        //     throw error_string;
        // }
    }
    auto model_receiver = glm::translate(model_1, structCopy<glm::vec3>(receiver_center_));
    model_receiver = model_receiver * scale_mat_receiver;
    auto pvm_receiver = persp * view * model_receiver;
    glBindVertexArray(receiver_index_.vao_);
    glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(pvm_receiver));
    glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(model_receiver));
    glUniform1i(8, 0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, receiver_texture_index_);
    switch (this->receiver_geometry_type_)
    {
    case GeometryType::kGeometryRectangle3D:
        glDrawElements(GL_TRIANGLES, rectangle_indices_.size(), GL_UNSIGNED_INT, nullptr);
        break;
    case GeometryType::kGeometryCylinder3D:
        glDrawElements(GL_TRIANGLES, 72 * 12, GL_UNSIGNED_INT, nullptr);
        break;
        // default:
        // {
        //     const auto error_string =
        //         ::solar::format("ERROR: In function {}:\n {} is not implemented",
        //         PRETTY_FUNCTION_NAME,
        //                         solar::getEnumName(this->receiver_geometry_type_));
        //     solar::println(error_string);
        //     throw error_string;
        // }
    }
    // glDrawElements(GL_TRIANGLES, 6 * 12, GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(NULL);
    glBindTexture(GL_TEXTURE_2D, NULL);
    glCullFace(GL_BACK);

    program_with_texture_->release();
    program_->bind();

    // draw axis
    glDisable(GL_CULL_FACE);
    light_ambient_ = {0.5, 0.5, 0.5, 1};
    glUniform4fv(3, 1, glm::value_ptr(light_ambient_));
    auto axis_translate_vec =
        glm::inverse(view) *
            glm::vec4(1.6 * (viewport_size_.toSizeF().width() / window_->devicePixelRatio() /
                             window_->size().toSizeF().width()),
                      -0.95, -3, 1) -
        glm::vec4(0, 0, 0, 1);
    // glm::mat4 scale_mat_axis = glm::scale(model_1, {0.5, 0.5, 0.5});
    glm::mat4 scale_mat_axis = glm::scale(model_1, {0.3, 0.3, 0.3});
    glm::mat4 model_axis = glm::translate(model_1, glm::vec3(axis_translate_vec));
    model_axis = model_axis * scale_mat_axis;
    auto pvm_axis = persp * view * model_axis;
    glBindVertexArray(axis_index_.vao_);
    glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(pvm_axis));
    glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(model_axis));
    glDisable(GL_LIGHTING);
    // glDisable(GL_DEPTH_TEST);
    glLineWidth(5.0F);
    glDrawArrays(GL_LINES, 0, axis_vertices_.size());
    glBindVertexArray(NULL);

    glLineWidth(2.0F);
    glEnable(GL_DEPTH_TEST);

    // draw sun rays;
    glm::mat4 scale_mat_sunray = glm::scale(model_1, {2, 2, 2});
    sun_ray_transform_mats_.resize(all_rectangle_heliostat_centers_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        auto current_center = all_rectangle_heliostat_centers_[i];

        auto model_sunray = glm::translate(
            model_1, glm::vec3(current_center.x, current_center.y, current_center.z));
        model_sunray = model_sunray * scale_mat_sunray;
        auto pvm_sunray = persp * view * model_sunray;
        sun_ray_transform_mats_[i] = std::make_pair(pvm_sunray, model_sunray);
    }
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        const auto& buffer_index = sunray_indices_[i];
        glBindVertexArray(buffer_index.vao_);
        glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(sun_ray_transform_mats_[i].first));
        glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(sun_ray_transform_mats_[i].second));
        glDrawArrays(GL_LINES, 0, sunray_vertices_.size());
        glBindVertexArray(NULL);
    }

    // draw reflect rays;
    glm::mat4 scale_mat_reflect_ray = glm::scale(model_1, {2, 2, 2});
    reflect_ray_transform_mats_.resize(all_rectangle_heliostat_centers_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        auto current_center = all_rectangle_heliostat_centers_[i];
        auto model_reflect_ray = glm::translate(
            model_1, glm::vec3(current_center.x, current_center.y, current_center.z));
        model_reflect_ray = model_reflect_ray * scale_mat_reflect_ray;
        auto pvm_reflect_ray = persp * view * model_reflect_ray;
        reflect_ray_transform_mats_[i] = std::make_pair(pvm_reflect_ray, model_reflect_ray);
    }
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        const auto& buffer_index = reflect_ray_indices_[i];
        glBindVertexArray(buffer_index.vao_);
        glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(reflect_ray_transform_mats_[i].first));
        glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(reflect_ray_transform_mats_[i].second));
        glDrawArrays(GL_LINES, 0, sunray_vertices_.size());
        glBindVertexArray(NULL);
    }

    light_ambient_ = {0.2, 0.2, 0.2, 1};
    glUniform4fv(3, 1, glm::value_ptr(light_ambient_));
    glLineWidth(0.2F);
    // glEnable(GL_LINE_STIPPLE);
    // glLineStipple(2, 0xAAAA);
    // draw grid axis x
    auto grid_x_center_begin = Float3{ground_center.x, 0, static_cast<float>(ground_position.z())};
    auto grid_z_interval =
        cpu::toFloat3(SOLAR_GET_VALUE(RectangleGrid, interval_, rectangle_grid_)).z;
    grid_axis_x_transform_mats_.resize(grid_axis_x_indices_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < grid_axis_x_indices_.size(); i++)
    {
        auto model_grid_axis_x = glm::translate(
            model_1, {grid_x_center_begin.x, 0.001, grid_x_center_begin.z + grid_z_interval * i});
        glm::mat4 scale_mat_grid_axis_x = glm::scale(model_1, {ground_size.x, 1, 1});
        model_grid_axis_x = model_grid_axis_x * scale_mat_grid_axis_x;
        auto pvm_grid_axis_x = persp * view * model_grid_axis_x;
        grid_axis_x_transform_mats_[i] = std::make_pair(pvm_grid_axis_x, model_grid_axis_x);
    }
    for (int i = 0; i < grid_axis_x_indices_.size(); i++)
    {
        const auto& buffer_index = grid_axis_x_indices_[i];
        glBindVertexArray(buffer_index.vao_);
        glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(grid_axis_x_transform_mats_[i].first));
        glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(grid_axis_x_transform_mats_[i].second));
        glDrawArrays(GL_LINES, 0, grid_axis_x_vertices_.size());
        glBindVertexArray(NULL);
    }

    // draw grid axis z
    auto grid_z_center_begin = Float3{static_cast<float>(ground_position.x()), 0, ground_center.z};
    auto grid_x_interval =
        cpu::toFloat3(SOLAR_GET_VALUE(RectangleGrid, interval_, rectangle_grid_)).x;
    grid_axis_z_transform_mats_.resize(grid_axis_z_indices_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < grid_axis_z_indices_.size(); i++)
    {
        auto model_grid_axis_z = glm::translate(
            model_1, {grid_z_center_begin.x + grid_x_interval * i, 0.001, grid_z_center_begin.z});
        glm::mat4 scale_mat_grid_axis_z = glm::scale(model_1, {1, 1, ground_size.z});
        model_grid_axis_z = model_grid_axis_z * scale_mat_grid_axis_z;
        auto pvm_grid_axis_z = persp * view * model_grid_axis_z;
        grid_axis_z_transform_mats_[i] = std::make_pair(pvm_grid_axis_z, model_grid_axis_z);
    }
    for (int i = 0; i < grid_axis_z_indices_.size(); i++)
    {
        const auto& buffer_index = grid_axis_z_indices_[i];
        glBindVertexArray(buffer_index.vao_);
        glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(grid_axis_z_transform_mats_[i].first));
        glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(grid_axis_z_transform_mats_[i].second));
        glDrawArrays(GL_LINES, 0, grid_axis_z_vertices_.size());
        glBindVertexArray(NULL);
    }
    glDisable(GL_LINE_STIPPLE);

    // draw skybox

    program_skybox_->bind();
    glDepthFunc(GL_LEQUAL);
    auto skybox_view_mat = glm::mat4(glm::mat3(view));
    glBindVertexArray(skybox_index_.vao_);
    glUniformMatrix4fv(glGetUniformLocation(program_skybox_->programId(), "view"), 1, GL_FALSE,
                       glm::value_ptr(skybox_view_mat));
    glUniformMatrix4fv(glGetUniformLocation(program_skybox_->programId(), "projection"), 1,
                       GL_FALSE, glm::value_ptr(persp));
    glUniform1i(glGetUniformLocation(program_skybox_->programId(), "skybox"), 0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_index_);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(NULL);
    glDepthFunc(GL_LESS);
    program_skybox_->release();

    // End OpenGL Specific functions

    render_lock.unlock();

    program_->release();

    window_->endExternalCommands();
}

auto SceneRenderer::makeRectangle(const std::vector<solar::cuda::inter_op::Float3>& vertices,
                                  const std::vector<solar::cuda::inter_op::Float3>& normals)
    -> std::vector<Vertex>
{
    std::vector<Vertex> result;
    result.resize(vertices.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (std::ptrdiff_t i = 0; i < vertices.size(); i++)
    {
        auto current_vertex = vertices[i];
        auto current_normal = normals[i % 4];
        result[i] = {
            {current_vertex.x, current_vertex.y, current_vertex.z},
            {current_normal.x, current_normal.y, current_normal.z},
            {0, 0},
            {0, 0, 1, 1}
        };
    }
    return result;
}

auto SceneRenderer::makeRectangle3D(const std::vector<solar::cuda::inter_op::Float3>& vertices,
                                    const std::vector<solar::cuda::inter_op::Float3>& normals,
                                    const std::vector<solar::cuda::inter_op::Float3>& sizes)
    -> std::vector<Vertex>
{
    std::vector<Vertex> result;
    result.resize(vertices.size() * 6);
    std::array<Vec3, 4> bottom_vertices;
    std::array<Vec3, 4> surface_vertices;
    std::array<Float3, 4> bottom_vertices1;
    std::array<Float3, 4> surface_vertices1;
    for (std::ptrdiff_t i = 0; i < normals.size(); i++)
    {
        auto vertex_begin_index = i * 4;
        auto result_begin_index = i * cube_vertices_.size();

        // 左下 左上 右上 右下

        for (std::ptrdiff_t j = 0; j < 4; j++)
        {
            auto current_vertex = cpu::toVec3(vertices[vertex_begin_index + j]);
            auto current_vertex_bottom = current_vertex - cpu::toVec3(normals[i]) * sizes[i].y;
            surface_vertices[j] = current_vertex;
            bottom_vertices[j] = current_vertex_bottom;
            surface_vertices1[j] = cpu::toFloat3(surface_vertices[j]);
            bottom_vertices1[j] = cpu::toFloat3(bottom_vertices[j]);
            // __builtin_dump_struct(&surface_vertices1[j], &printf);
            // __builtin_dump_struct(&bottom_vertices1[j], &printf);
        }
        auto rectangle_left_vec = surface_vertices[1] - surface_vertices[0];
        auto rectangle_right_vec = surface_vertices[3] - surface_vertices[2];
        auto rectangle_up_vec = surface_vertices[2] - surface_vertices[1];
        auto rectangle_down_vec = surface_vertices[0] - surface_vertices[3];

        Float3 up_normal = normals[i];
        auto up_normal1 = cpu::toVec3(up_normal);
        auto down_normal = cpu::toFloat3(-up_normal1);
        auto left_normal = cpu::toFloat3(cross(up_normal1, rectangle_left_vec).get_unit_vector());
        auto front_normal = cpu::toFloat3(cross(up_normal1, rectangle_down_vec).get_unit_vector());
        auto right_normal = cpu::toFloat3(cross(up_normal1, rectangle_right_vec).get_unit_vector());
        auto back_normal = cpu::toFloat3(cross(up_normal1, rectangle_up_vec).get_unit_vector());

        // 前

        result[result_begin_index] = {
            {bottom_vertices1[0].x, bottom_vertices1[0].y, bottom_vertices1[0].z},
            {front_normal.x, front_normal.y, front_normal.z},
            {0, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 1] = {
            {bottom_vertices1[3].x, bottom_vertices1[3].y, bottom_vertices1[3].z},
            {front_normal.x, front_normal.y, front_normal.z},
            {1, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 2] = {
            {surface_vertices1[3].x, surface_vertices1[3].y, surface_vertices1[3].z},
            {front_normal.x, front_normal.y, front_normal.z},
            {1, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 3] = {
            {surface_vertices1[0].x, surface_vertices1[0].y, surface_vertices1[0].z},
            {front_normal.x, front_normal.y, front_normal.z},
            {0, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };

        // 后

        result[result_begin_index + 4] = {
            {bottom_vertices1[1].x, bottom_vertices1[1].y, bottom_vertices1[1].z},
            {back_normal.x, back_normal.y, back_normal.z},
            {0, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 5] = {
            {surface_vertices1[1].x, surface_vertices1[1].y, surface_vertices1[1].z},
            {back_normal.x, back_normal.y, back_normal.z},
            {1, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 6] = {
            {surface_vertices1[2].x, surface_vertices1[2].y, surface_vertices1[2].z},
            {back_normal.x, back_normal.y, back_normal.z},
            {1, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 7] = {
            {bottom_vertices1[2].x, bottom_vertices1[2].y, bottom_vertices1[2].z},
            {back_normal.x, back_normal.y, back_normal.z},
            {0, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };

        // 下

        result[result_begin_index + 8] = {
            {bottom_vertices1[0].x, bottom_vertices1[0].y, bottom_vertices1[0].z},
            {down_normal.x, down_normal.y, down_normal.z},
            {0, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 9] = {
            {bottom_vertices1[1].x, bottom_vertices1[1].y, bottom_vertices1[1].z},
            {down_normal.x, down_normal.y, down_normal.z},
            {1, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 10] = {
            {bottom_vertices1[2].x, bottom_vertices1[2].y, bottom_vertices1[2].z},
            {down_normal.x, down_normal.y, down_normal.z},
            {1, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 11] = {
            {bottom_vertices1[3].x, bottom_vertices1[3].y, bottom_vertices1[3].z},
            {down_normal.x, down_normal.y, down_normal.z},
            {0, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };

        // 右

        result[result_begin_index + 12] = {
            {bottom_vertices1[3].x, bottom_vertices1[3].y, bottom_vertices1[3].z},
            {right_normal.x, right_normal.y, right_normal.z},
            {0, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 13] = {
            {bottom_vertices1[2].x, bottom_vertices1[2].y, bottom_vertices1[2].z},
            {right_normal.x, right_normal.y, right_normal.z},
            {1, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 14] = {
            {surface_vertices1[2].x, surface_vertices1[2].y, surface_vertices1[2].z},
            {right_normal.x, right_normal.y, right_normal.z},
            {1, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 15] = {
            {surface_vertices1[3].x, surface_vertices1[3].y, surface_vertices1[3].z},
            {right_normal.x, right_normal.y, right_normal.z},
            {0, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };

        // 上

        result[result_begin_index + 16] = {
            {surface_vertices1[3].x, surface_vertices1[3].y, surface_vertices1[3].z},
            {up_normal.x, up_normal.y, up_normal.z},
            {0, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 17] = {
            {surface_vertices1[2].x, surface_vertices1[2].y, surface_vertices1[2].z},
            {up_normal.x, up_normal.y, up_normal.z},
            {1, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 18] = {
            {surface_vertices1[1].x, surface_vertices1[1].y, surface_vertices1[1].z},
            {up_normal.x, up_normal.y, up_normal.z},
            {1, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 19] = {
            {surface_vertices1[0].x, surface_vertices1[0].y, surface_vertices1[0].z},
            {up_normal.x, up_normal.y, up_normal.z},
            {0, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };

        // 左

        result[result_begin_index + 20] = {
            {surface_vertices1[0].x, surface_vertices1[0].y, surface_vertices1[0].z},
            {left_normal.x, left_normal.y, left_normal.z},
            {0, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 21] = {
            {surface_vertices1[1].x, surface_vertices1[1].y, surface_vertices1[1].z},
            {left_normal.x, left_normal.y, left_normal.z},
            {1, 0},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 22] = {
            {bottom_vertices1[1].x, bottom_vertices1[1].y, bottom_vertices1[1].z},
            {left_normal.x, left_normal.y, left_normal.z},
            {1, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
        result[result_begin_index + 23] = {
            {bottom_vertices1[0].x, bottom_vertices1[0].y, bottom_vertices1[0].z},
            {left_normal.x, left_normal.y, left_normal.z},
            {0, 1},
            {110.0 / 255, 189.0 / 255, 1, 1}
        };
    }
    return result;
}

void SceneRenderer::rotateLookAtCenterHorizontal(float degree)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    glm::mat4 R(1.0F);
    R = glm::rotate(R, glm::radians(degree), glm::vec3(0, 1, 0));
    glm::vec4 center_copy = {this->center_, 1};
    center_copy = center_copy * R;
    render_lock.lock();
    this->center_ = center_copy;
    render_lock.unlock();
}

void SceneRenderer::rotateLookAtCenterVertical(float degree)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    glm::mat4 R(1.0F);
    R = glm::rotate(R, glm::radians(degree), glm::vec3(1, 0, 0));
    glm::vec4 center_copy = {this->center_, 1};
    center_copy = center_copy * R;
    render_lock.lock();
    this->center_ = center_copy;
    render_lock.unlock();
}

void SceneRenderer::rotateLookAtCenter(float horizontal_degree, float vertical_degree)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    glm::mat4 R(1.0F);
    R = glm::rotate(R, glm::radians(vertical_degree), glm::vec3(1, 0, 0));
    glm::vec4 center_copy = {this->center_, 1};
    center_copy = center_copy * R;
    R = glm::rotate(R, glm::radians(horizontal_degree), glm::vec3(0, 1, 0));
    center_copy = center_copy * R;
    render_lock.lock();
    this->center_ = center_copy;
    render_lock.unlock();
}

#define DELETE_OPENGL_BUFFER_INDEX(buffer_index)                                                   \
    glDeleteVertexArrays(1, std::addressof((buffer_index).vao_));                                  \
    glDeleteBuffers(1, std::addressof((buffer_index).vbo_));                                       \
    glDeleteBuffers(1, std::addressof((buffer_index).ebo_));

#define DELETE_OPENGL_BUFFER_INDICES_CONTAINER(buffer_indices_container)                           \
    for (const auto& buffer_index : (buffer_indices_container))                                    \
    {                                                                                              \
        DELETE_OPENGL_BUFFER_INDEX(buffer_index)                                                   \
    }

void SceneRenderer::deleteAllOpenGLBufferIndices()
{
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(heliostat_buffer_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(heliostat_pillar_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(reflect_ray_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(sunray_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(grid_axis_x_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(grid_axis_z_indices_)
}

void SceneRenderer::deleteOpenGLBufferIndicesForSunray()
{
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(heliostat_buffer_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(reflect_ray_indices_)
    DELETE_OPENGL_BUFFER_INDICES_CONTAINER(sunray_indices_)
}

#undef DELETE_OPENGL_BUFFER_INDICES_CONTAINER
#undef DELETE_OPENGL_BUFFER_INDEX

void SceneRenderer::updateScene(AnalyticalPipelineParam* params)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    // std::cout << std::this_thread::get_id() << '\n';
    // std::cout << "updating\n";
    render_lock.lock();
    this->num_heliostats_ = params->heliostats_.size();
    all_rectangle_heliostat_normals_.resize(params->heliostats_.size());
    all_rectangle_heliostat_sizes_.resize(params->heliostats_.size());
    all_rectangle_heliostat_centers_.resize(params->heliostats_.size());

    auto receiver_geometry = SOLAR_GET_VALUE(Receiver, geometry_, params->receivers_[0]);
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (std::ptrdiff_t i = 0; i < params->heliostats_.size(); i++)
    {
        auto* rectangle_geometry = dynamic_cast<Rectangle3D*>(
            (SOLAR_GET_VALUE(Heliostat, geometry_, params->heliostats_[i])).get());
        auto center = cpu::toFloat3(solar::getCenter(rectangle_geometry));
        auto current_focus_center = getFocusCenter(rectangle_geometry, receiver_geometry.get());
        auto normal = cpu::setNormal(center, cpu::toFloat3(current_focus_center), sunray_dir_);
        auto size = cpu::toFloat3(SOLAR_GET_VALUE(Rectangle3D, size_, *rectangle_geometry));
        all_rectangle_heliostat_normals_[i] = normal;
        all_rectangle_heliostat_sizes_[i] = size;
        all_rectangle_heliostat_centers_[i] = center;
    }

    heliostat_buffer_indices_.resize(params->heliostats_.size());
    heliostat_pillar_indices_.resize(params->heliostats_.size());
    sunray_indices_.resize(params->heliostats_.size());
    reflect_ray_indices_.resize(params->heliostats_.size());

    this->receiver_geometry_type_ = receiver_geometry->getType();
    switch (this->receiver_geometry_type_)
    {
    case GeometryType::kGeometryRectangle3D:
    {
        auto receiver_geometry_rectangle3d = dynamic_cast<Rectangle3D*>(receiver_geometry.get());
        this->receiver_center_ =
            cpu::toFloat3(SOLAR_GET_VALUE(Rectangle3D, center_, *receiver_geometry_rectangle3d));
        this->receiver_size_ =
            cpu::toFloat3(SOLAR_GET_VALUE(Rectangle3D, size_, *receiver_geometry_rectangle3d));
        break;
    }
    case GeometryType::kGeometryCylinder3D:
    {
        auto receiver_geometry_cylinder3d = dynamic_cast<Cylinder3D*>(receiver_geometry.get());
        float radius = SOLAR_GET_VALUE(Cylinder3D, radius_, *receiver_geometry_cylinder3d);
        float height = SOLAR_GET_VALUE(Cylinder3D, height_, *receiver_geometry_cylinder3d);
        auto diameter = radius * 2;
        this->receiver_center_ =
            cpu::toFloat3(SOLAR_GET_VALUE(Cylinder3D, center_, *receiver_geometry_cylinder3d));
        this->receiver_size_ = {diameter, height, diameter};
        break;
    }
    // Prism ??（目前不存在这个Geometry）
    default:
    {
        const auto error_string =
            ::solar::format("ERROR: In function {}:\n {} is not implemented", PRETTY_FUNCTION_NAME,
                            solar::getEnumName(this->receiver_geometry_type_));
        solar::println(error_string);
        throw error_string;
    }
    }

    this->reflect_vertices_vec_.resize(params->heliostats_.size());
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < params->heliostats_.size(); i++)
    {
        auto reflect_ray_vec = cpu::toFloat3(
            (cpu::toVec3(focus_center_) - cpu::toVec3(all_rectangle_heliostat_centers_[i]))
                .get_unit_vector());
        std::array reflect_vertices = {
            Vertex{                                                {0, 0, 0},{0, 0, 0},{0, 0},{1, 0, 0, 1}               },
            Vertex{{reflect_ray_vec.x, reflect_ray_vec.y, reflect_ray_vec.z},
                   {0, 0, 0},
                   {0, 0},
                   {1, 0, 0, 1}}
        };
        reflect_vertices_vec_[i] = reflect_vertices;
    }

    this->rectangle_grid_ = params->rectangle_grid_;

    this->scene_updated_ = true;
    // std::cout << "updated\n";
    render_lock.unlock();
}

void SceneRenderer::updateSceneForSunray()
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    // std::cout << std::this_thread::get_id() << '\n';
    // std::cout << "updating\n";
    render_lock.lock();
    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (std::ptrdiff_t i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        auto normal =
            cpu::setNormal(all_rectangle_heliostat_centers_[i], focus_center_, sunray_dir_);
        all_rectangle_heliostat_normals_[i] = normal;
    }

    this->sunray_vertices_[1] = {
        {-sunray_dir_.x, -sunray_dir_.y, -sunray_dir_.z},
        {0, 0, 0},
        {0, 0},
        {1, 231.0 / 255, 110.0 / 255, 1}
    };

    SOLAR_OMP_PARALLEL_FOR_SIMD
    for (int i = 0; i < all_rectangle_heliostat_centers_.size(); i++)
    {
        auto reflect_ray_vec = cpu::toFloat3(
            (cpu::toVec3(focus_center_) - cpu::toVec3(all_rectangle_heliostat_centers_[i]))
                .get_unit_vector());
        std::array reflect_vertices = {
            Vertex{                                                {0, 0, 0},{0, 0, 0},{0, 0},{1, 0, 0, 1}               },
            Vertex{{reflect_ray_vec.x, reflect_ray_vec.y, reflect_ray_vec.z},
                   {0, 0, 0},
                   {0, 0},
                   {1, 0, 0, 1}}
        };
        reflect_vertices_vec_[i] = reflect_vertices;
    }

    this->scene_updated_for_sunray_ = true;
    // std::cout << "updated\n";
    render_lock.unlock();
}

void SceneRenderer::updateSceneInternal()
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    render_lock.lock();

    for (int i = 0; i < heliostat_buffer_indices_.size(); i++)
    {
        loadOpenGLFaceVertices(heliostat_buffer_indices_[i], cube_heliostat_vertices_,
                               rectangle3d_indices_);
    }

    // Don't parallelize this loop, since OpenGL doesn't support it.
    for (int i = 0, j = 0; i < num_heliostats_; i++, j++)
    {
        loadOpenGLFaceVertices(heliostat_pillar_indices_[j], cube_vertices_, rectangle3d_indices_);
    }

    static constexpr auto cylinder_vertices = generatePolygonFullVertices<72>();
    static constexpr auto cylinder_indices = generatePolygonFullVertexIndices<72>();
    // static constexpr auto prism6_vertices = generatePolygonFullVertices<6>();
    // static constexpr auto prism6_indices = generatePolygonFullVertexIndices<6>();

    switch (this->receiver_geometry_type_)
    {
    case GeometryType::kGeometryRectangle3D:
    {
        loadOpenGLFaceVertices(receiver_index_, square_vertices_, rectangle_indices_);
        break;
    }
    case GeometryType::kGeometryCylinder3D:
    {
        loadOpenGLFaceVertices(receiver_index_, cylinder_vertices, cylinder_indices);
        break;
    }
    // Prism ??（目前不存在这个Geometry）
    default:
    {
        const auto error_string =
            ::solar::format("ERROR: In function {}:\n {} is not implemented", PRETTY_FUNCTION_NAME,
                            solar::getEnumName(this->receiver_geometry_type_));
        solar::println(error_string);
        throw error_string;
    }
    }

    // Don't parallelize this loop, since OpenGL doesn't support it.
    for (int i = 0, j = 0; i < num_heliostats_; i++, j++)
    {
        loadOpenGLLineVertices(sunray_indices_[j], sunray_vertices_);
    }

    // Don't parallelize this loop, since OpenGL doesn't support it.
    for (int i = 0, j = 0; i < num_heliostats_; i++, j++)
    {
        loadOpenGLLineVertices(reflect_ray_indices_[j], reflect_vertices_vec_[i]);
    }

    auto grid_size = cpu::toFloat3(SOLAR_GET_VALUE(RectangleGrid, size_, rectangle_grid_));
    auto grid_interval = cpu::toFloat3(SOLAR_GET_VALUE(RectangleGrid, interval_, rectangle_grid_));
    const auto grid_axis_x_count =
        static_cast<std::uint64_t>(std::floor(grid_size.x / grid_interval.x) + 1);
    const auto grid_axis_z_count =
        static_cast<std::uint64_t>(std::floor(grid_size.z / grid_interval.z) + 1);
    grid_axis_x_indices_.resize(grid_axis_x_count);
    grid_axis_z_indices_.resize(grid_axis_z_count);

    // Don't parallelize this loop, since OpenGL doesn't support it.
    for (int i = 0, j = 0; i < grid_axis_x_count; i++, j++)
    {
        loadOpenGLLineVertices(grid_axis_x_indices_[j], grid_axis_x_vertices_);
    }

    // Don't parallelize this loop, since OpenGL doesn't support it.
    for (int i = 0, j = 0; i < grid_axis_z_count; i++, j++)
    {
        loadOpenGLLineVertices(grid_axis_z_indices_[j], grid_axis_z_vertices_);
    }

    // receiver 纹理
    // It's a fake texture now, should be replaced with the real one

    glGenTextures(1, &receiver_texture_index_);
    glBindTexture(GL_TEXTURE_2D, receiver_texture_index_);
    // 设置环绕
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // 过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    QImage receiver_texture_image(":/image/scene_renderer_icon/file_map.png");
    const QImage receiver_texture_image1 =
        receiver_texture_image.convertedTo(QImage::Format_RGB888);
    const auto* texture_data = receiver_texture_image1.bits();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, receiver_texture_image1.width(),
                 receiver_texture_image1.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data);
    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

    this->scene_updated_ = false;
    render_lock.unlock();
}

void SceneRenderer::updateSceneForSunrayInternal()
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    render_lock.lock();

    if (sunray_indices_.size() > 0) [[likely]]
    {
        glBindBuffer(GL_ARRAY_BUFFER, sunray_indices_[0].vbo_);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sunray_vertices_.size() * sizeof(Vertex),
                        sunray_vertices_.data());
        for (int i = 1, j = 1; i < num_heliostats_; i++, j++)
        {
            glBindBuffer(GL_COPY_READ_BUFFER, sunray_indices_[0].vbo_);
            glBindBuffer(GL_COPY_WRITE_BUFFER, sunray_indices_[j].vbo_);
            glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0,
                                sizeof(Vertex) * sunray_vertices_.size());
        }
    }

    this->scene_updated_for_sunray_ = false;
    render_lock.unlock();
}

void SceneRenderer::setFocusCenter(const solar::cuda::inter_op::Float3& focus_center)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    render_lock.lock();
    this->focus_center_ = focus_center;
    render_lock.unlock();
}

void SceneRenderer::setSunrayDirection(const solar::cuda::inter_op::Float3& sunray_dir)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    render_lock.lock();
    this->sunray_dir_ = sunray_dir;
    structCopy(sunray_dir, this->light_direction_);
    render_lock.unlock();
}

void SceneRenderer::moveCameraCenter(float delta_x, float delta_y, float delta_z)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    glm::vec3 delta = {delta_x, delta_y, delta_z};
    render_lock.lock();
    this->eye_ += delta;
    render_lock.unlock();
}

void SceneRenderer::moveLookAtCenter(float delta_x, float delta_y, float delta_z)
{
    std::shared_lock render_lock(this->update_data_mutex_, std::defer_lock);
    glm::vec3 delta = {delta_x, delta_y, delta_z};
    render_lock.lock();
    this->center_ += delta;
    render_lock.unlock();
}

auto SceneRenderer::generatePolygonVertices(int num_edges) -> std::vector<Vertex>
{
    std::vector<Vertex> result(num_edges * 4);
    std::vector<float> polygon_vertices(num_edges * 2);
    std::vector<float> polygon_normals(num_edges * 2);
    generatePolygonVertexPositions(num_edges, polygon_vertices.data());
    generatePolygonFaceNormals(num_edges, polygon_normals.data());
    generatePolygonVertices1(num_edges, polygon_vertices.data(), polygon_normals.data(),
                             result.data());
    return result;
}

auto SceneRenderer::generatePolygonFullVertices(int num_edges) -> std::vector<Vertex>
{
    std::vector<Vertex> result(num_edges * 6 + 2);
    std::vector<float> polygon_vertices(num_edges * 2);
    std::vector<float> polygon_normals(num_edges * 2);
    generatePolygonVertexPositions(num_edges, polygon_vertices.data());
    generatePolygonFaceNormals(num_edges, polygon_normals.data());
    generatePolygonVertices1(num_edges, polygon_vertices.data(), polygon_normals.data(),
                             result.data());
    generatePolygonCeilAndFloorVertices1(num_edges, polygon_vertices.data(),
                                         result.data() + num_edges * 4);
    return result;
}

auto SceneRenderer::generatePolygonVertexIndices(int num_edges) -> std::vector<GLuint>
{
    static constexpr std::array indices_0 = {0U, 1U, 3U, 2U, 3U, 1U};
    std::vector<GLuint> result(num_edges * indices_0.size());
    generatePolygonVertexIndicesInternal(num_edges, result.data());
    return result;
}

auto SceneRenderer::generatePolygonFullVertexIndices(int num_edges) -> std::vector<GLuint>
{
    std::vector<GLuint> result(num_edges * 12);
    generatePolygonFullVertexIndicesInternal(num_edges, result.data());
    return result;
}

auto SceneRenderer::generateSphereVertices(int segments) -> std::vector<Vertex>
{
    std::vector<Vertex> sphere_vertices;
    generateSphereVerticesInternal(segments, sphere_vertices.data());
    return sphere_vertices;
}

auto SceneRenderer::generateSphereIndices(int segments) -> std::vector<GLuint>
{
    std::vector<GLuint> sphere_indices;
    generateSphereIndicesInternal(segments, sphere_indices.data());
    return sphere_indices;
}
